package com.dayatang.hrm.compensation.domain;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;

import com.dayatang.commons.domain.AbstractEntity;
import com.dayatang.commons.domain.DataType;
import com.dayatang.commons.domain.InstanceFactory;
import com.dayatang.hrm.compensation.context.CompensationMessageSource;
import com.dayatang.hrm.compensation.domain.exception.CompensationRuntimeException;
import com.dayatang.hrm.compensation.policy.RoundingModePolicy;
import com.dayatang.hrm.compensation.vo.NumberScale;
import com.dayatang.hrm.compensation.vo.RealBigDecimalValue;
import com.dayatang.hrm.compensation.vo.RealBooleanValue;
import com.dayatang.hrm.compensation.vo.RealDateValue;
import com.dayatang.hrm.compensation.vo.RealDoubleValue;
import com.dayatang.hrm.compensation.vo.RealIntValue;
import com.dayatang.hrm.compensation.vo.RealLongValue;
import com.dayatang.hrm.compensation.vo.RealStringValue;
import com.dayatang.hrm.compensation.vo.RealTimeValue;
import com.dayatang.hrm.compensation.vo.RealTimestampValue;
import com.dayatang.hrm.compensation.vo.RealValue;
import com.dayatang.utils.DateUtils;

/**
 * 薪资计算项目
 * 
 * @author chencao
 * 
 */
@Entity
@Table(name = "pay_salary_cal_item")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "item_category", discriminatorType = DiscriminatorType.INTEGER)
@DiscriminatorValue("1")
@NamedQueries( { @NamedQuery(name = "SalaryCalculateItem.findSalaryCalculateItemsBySalaryAccountSuite", query = "select new SalaryCalculateItem(o.abolishDate, suite, setting) from SalaryCalculateItemSetting setting inner join setting.salaryCalItem o inner join o.salaryAccountSuite suite where (suite = :pSuite and o.abolishDate >= :pQueryDate and  setting.fromDate <= :pQueryDate and setting.toDate >= :pQueryDate)") })
public class SalaryCalculateItem extends AbstractEntity {

	/**
	 * 
	 */
	private static final long serialVersionUID = 197455157442605990L;

	private static final Logger logger = LoggerFactory
			.getLogger(SalaryCalculateItem.class);

	private static SalaryCalculateItemRepository salaryCalItemRepository;

	protected static MessageSourceAccessor messages = CompensationMessageSource
			.getAccessor();

	// 废除日期
	@Column(name = "abolish_date")
	@Temporal(TemporalType.TIMESTAMP)
	private Date abolishDate = DateUtils.MAX_DATE;

	@ManyToOne
	@JoinColumn(name = "salary_accountsuite_id")
	private SalaryAccountSuite salaryAccountSuite;

	/*
	 * =======================================
	 * 
	 * transient
	 * 
	 * ========================================
	 */
	/**
	 * latestSetting为薪资计算项目具体设置的一个代理
	 */
	@Transient
	private SalaryCalculateItemSetting latestSetting;
	@Transient
	private Object value;

	public SalaryCalculateItem() {
		latestSetting = SalaryCalculateItemSetting.init(this);
	}

	public SalaryCalculateItem(Date abolishDate,
			SalaryAccountSuite salaryAccountSuite,
			SalaryCalculateItemSetting latestSetting) {
		this.abolishDate = abolishDate;
		this.salaryAccountSuite = salaryAccountSuite;
		this.latestSetting = latestSetting;
	}

	public static SalaryCalculateItemRepository getSalaryCalItemRepository() {
		if (salaryCalItemRepository == null) {
			salaryCalItemRepository = InstanceFactory
					.getInstance(SalaryCalculateItemRepository.class);
		}
		return salaryCalItemRepository;
	}

	public static void setSalaryCalItemRepository(
			SalaryCalculateItemRepository salaryCalItemRepository) {
		SalaryCalculateItem.salaryCalItemRepository = salaryCalItemRepository;
	}

	public static MessageSourceAccessor getMessages() {
		return messages;
	}

	public static void setMessages(MessageSourceAccessor messages) {
		SalaryCalculateItem.messages = messages;
	}

	public SalaryAccountSuite getSalaryAccountSuite() {
		return salaryAccountSuite;
	}

	public void setSalaryAccountSuite(SalaryAccountSuite salaryAccountSuite) {
		if (isNew()) {
			this.salaryAccountSuite = salaryAccountSuite;
		} else {
			throw new CompensationRuntimeException(messages.getMessage(
					"SalaryCalculateItem.setSalaryAccountSuite.couldNotModify",
					"不能修改薪资计算项目所属的薪资帐套。"));
		}
	}

	public Date getAbolishDate() {
		return abolishDate;
	}

	public void setAbolishDate(Date abolishDate) {
		this.abolishDate = abolishDate;
	}

	SalaryCalculateItemSetting getLatestSetting() {
		return latestSetting;
	}

	void setLatestSetting(SalaryCalculateItemSetting latestSetting) {
		this.latestSetting = latestSetting;
	}

	public Object getValue() {
		// if (value == null) {
		// return getDefaultValue().getRealValue();
		// }
		// return value.getRealValue();

		if (value == null) {
			return getDefaultValue();
		}
		return value;
	}

	public void setValue(Object value) {
		// this.value = RealValue.valueOf(value);
		this.value = value;
	}

	// 代理属性
	public String getName() {
		return latestSetting.getName();
	}

	public void setName(String name) {
		latestSetting.setName(name);
	}

	public DataType getDataType() {
		return latestSetting.getDataType();
	}

	public void setDataType(DataType dataType) {
		latestSetting.setDataType(dataType);
	}

	public Integer getOrderSeq() {
		return latestSetting.getOrderSeq();
	}

	public void setOrderSeq(Integer orderSeq) {
		latestSetting.setOrderSeq(orderSeq);
	}

	public Integer getLevel() {
		return latestSetting.getLevel();
	}

	public void setLevel(Integer level) {
		latestSetting.setLevel(level);
	}

	public RoundingModePolicy getRoundingModePolicy() {
		return latestSetting.getRoundingModePolicy();
	}

	public void setRoundingModePolicy(RoundingModePolicy roundingModePolicy) {
		latestSetting.setRoundingModePolicy(roundingModePolicy);
	}

	public NumberScale getCalculateScale() {
		return latestSetting.getCalculateScale();
	}

	public void setCalculateScale(NumberScale calculateScale) {
		latestSetting.setCalculateScale(calculateScale);
	}

	public NumberScale getDisplayScale() {
		return latestSetting.getDisplayScale();
	}

	public void setDisplayScale(NumberScale displayScale) {
		latestSetting.setDisplayScale(displayScale);
	}

	public String getRemark() {
		return latestSetting.getRemark();
	}

	public void setRemark(String remark) {
		latestSetting.setRemark(remark);
	}

	public Date getCreated() {
		return latestSetting.getCreated();
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof SalaryCalculateItem == false) {
			return false;
		}
		if (this == obj) {
			return true;
		}
		SalaryCalculateItem rhs = (SalaryCalculateItem) obj;
		return new EqualsBuilder().append(getId(), rhs.getId()).isEquals();
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder(17, 37).append(getId()).toHashCode();
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this,
				ToStringStyle.SHORT_PREFIX_STYLE);
	}

	/*
	 * =======================================
	 * 
	 * action
	 * 
	 * ========================================
	 */

	private void save() {
		Assert.notNull(salaryAccountSuite, messages.getMessage(
				"SalaryCalculateItem.save.suiteIsNull", "薪资计算项目所属帐套不能为空。"));
		getSalaryCalItemRepository().save(this);
	}

	/**
	 * 改正设置并保存（不会将设置备份）
	 */
	public void saveChanges() {
		save();
		latestSetting.saveChanges();
	}

	/**
	 * 备份原有设置并保存当前设置（会将之前的设置备份）
	 */
	public void archive() {
		save();
		latestSetting.archive();
	}

	public void markArchive() {
		latestSetting.markArchive();
	}

	public void markArchiveAll() {
		markArchive();

		List<CalculateRule> calRules = getCalculateRules(new Date());
		for (CalculateRule rule : calRules) {
			rule.markArchive(false);
		}

	}

	void remove() {
		getSalaryCalItemRepository().remove(this);
	}

	/**
	 * 在指定日期撤销薪资项目
	 * 
	 * @param abolishDate
	 */
	public void abolish(Date abolishDate) {
		if (abolishDate == null) {
			abolishDate = new Date();
		}

		if (logger.isDebugEnabled()) {
			logger.debug("废除薪资计算项目【{}】，废除日期【{}】", new Object[] { getName(),
					abolishDate });
		}

		setAbolishDate(abolishDate);
		save();

		latestSetting.abolish(abolishDate);

	}

	/**
	 * 根据指定日期，查找有效的薪资公式集合
	 * 
	 * @param queryDate
	 * @return
	 */
	public List<CalculateRule> getCalculateRules(Date queryDate) {
		return CalculateRule.findBySalaryCalculateItem(this, queryDate);
	}

	/**
	 * 根据指定日期，查找薪资计算项目所依赖的其他薪资计算项目的集合
	 * 
	 * @param queryDate
	 * @param recursive
	 *            是否递归查找
	 * @return
	 */
	public Set<SalaryCalculateItem> getDependentSalaryCalculateItems(
			Date queryDate, boolean recursive) {

		Set<SalaryCalculateItem> dependentSalaryCalculateItems = new HashSet<SalaryCalculateItem>();

		List<CalculateRule> calculateRules = getCalculateRules(queryDate);
		for (CalculateRule rule : calculateRules) {
			dependentSalaryCalculateItems.addAll(rule
					.getDependentSalaryCalculateItems(queryDate));
		}

		// 递归查找
		if (recursive) {
			for (SalaryCalculateItem calItem : dependentSalaryCalculateItems) {
				dependentSalaryCalculateItems
						.addAll(calItem.getDependentSalaryCalculateItems(
								queryDate, recursive));
			}
		}

		return dependentSalaryCalculateItems;
	}

	/**
	 * 在指定日期下，薪资计算项目的计算公式中是否存在循环依赖的情况
	 * 
	 * @param queryDate
	 * @return
	 */
	public boolean isCircleDependent(Date queryDate) {
		Set<SalaryCalculateItem> dependentSalaryCalculateItems = getDependentSalaryCalculateItems(
				queryDate, true);
		if (dependentSalaryCalculateItems.contains(this)) {
			return true;
		}
		return false;
	}

	public Object getDefaultValue() {

		RealValue defaultValue = null;

		if (getDataType().equals(DataType.BIG_DECIMAL)) {
			defaultValue = new RealBigDecimalValue();
			defaultValue.setValue("0");
		} else if (getDataType().equals(DataType.BOOLEAN)) {
			defaultValue = new RealBooleanValue();
			defaultValue.setValue("false");
		} else if (getDataType().equals(DataType.DATE)) {
			defaultValue = new RealDateValue();
			defaultValue.setValue("1970-01-01");
		} else if (getDataType().equals(DataType.DATE_TIME)) {
			defaultValue = new RealTimestampValue();
			defaultValue.setValue("1970-01-01 00:00:00");
		} else if (getDataType().equals(DataType.DOUBLE)) {
			defaultValue = new RealDoubleValue();
			defaultValue.setValue("0");
		} else if (getDataType().equals(DataType.INT)) {
			defaultValue = new RealIntValue();
			defaultValue.setValue("0");
		} else if (getDataType().equals(DataType.LONG)) {
			defaultValue = new RealLongValue();
			defaultValue.setValue("0");
		} else if (getDataType().equals(DataType.STRING)) {
			defaultValue = new RealStringValue();
			defaultValue.setValue("");
		} else if (getDataType().equals(DataType.TIME)) {
			defaultValue = new RealTimeValue();
			defaultValue.setValue("00:00:00");
		} else {
			throw new CompensationRuntimeException(messages.getMessage(
					"薪资计算项目的数据类型非法。",
					"SalaryCalculateItem.getDefaultValue.dataTypeError"));
		}

		defaultValue.setDataType(getDataType());

		return defaultValue.getRealValue();
	}

	/**
	 * 找到指定日期下有效的薪资项目（填充薪资项目设置信息）
	 * 
	 * @param id
	 * @param queryDate
	 * @return
	 */
	public static SalaryCalculateItem get(Long id, Date queryDate) {
		SalaryCalculateItem item = getSalaryCalItemRepository().get(id);
		if (item != null) {
			SalaryCalculateItemSetting setting = SalaryCalculateItemSetting
					.get(item, queryDate);
			if (setting == null) {
				// setting = SalaryCalculateItemSetting.init(item);
				throw new CompensationRuntimeException(messages.getMessage(
						"SalaryCalculateItem.get.notExist", "该日期下的薪资计算项目不存在。"));
			}
			item.setLatestSetting(setting);
		} else {
			throw new CompensationRuntimeException(messages.getMessage(
					"SalaryCalculateItem.get.notExist", "该日期下的薪资计算项目不存在。"));
		}
		return item;
	}

	public static SalaryCalculateItem getLatest(Long id) {
		return get(id, new Date());
	}

	/**
	 * 找到指定日期下，薪资帐套下有效的薪资计算项目集合
	 * 
	 * @param salaryAccountSuite
	 * @param queryDate
	 * @return
	 */
	public static List<SalaryCalculateItem> findSalaryCalculateItemsBySalaryAccountSuite(
			SalaryAccountSuite salaryAccountSuite, Date queryDate) {
		return getSalaryCalItemRepository()
				.findSalaryCalculateItemsBySalaryAccountSuite(
						salaryAccountSuite, queryDate);
	}

}
